#include <boost/iostreams/code_converter.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <algorithm>

#include <iostream>
#include <fstream>
#include <stdexcept>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
#include "count_lines_in_file.h"

namespace io = boost::iostreams;


std::streamsize  count_lines_in_file_std(   const std::string&                  file_name,
											char                            eol = '\n')
{
	std::ifstream in(file_name.c_str());
	if (!in)
		throw std::runtime_error("Could not open " + file_name);
	return std::count(std::istreambuf_iterator<char>(in),
					  std::istreambuf_iterator<char>(), eol);
}

std::streamsize  count_lines_in_file(   const std::string&              file_name,
										char                            eol)
{
	try
	{
		io::mapped_file_source mapfile;
		mapfile.open(file_name);
		return std::count(mapfile.data(),  mapfile.data() + mapfile.size(), eol);
	}
	catch ( std::ios::failure& fail )
	{
		// probably file too large.
	}

	return count_lines_in_file_std( file_name,
									eol);
}


template<typename INPUTITERATOR>
typename std::iterator_traits<INPUTITERATOR>::difference_type
count_fasta(INPUTITERATOR first, INPUTITERATOR last)
{
	// concept requirements
	typename std::iterator_traits<INPUTITERATOR>::difference_type cnt = 0;
	typename std::iterator_traits<INPUTITERATOR>::value_type prev = '\n';
	for ( ; first != last; ++first)
	{
		if (*first == '>' && prev == '\n')
			++cnt;
		prev = *first;
	}
	if (prev == '>')
		--cnt;
	return cnt;
}



std::streamsize  estimate_fasta_seq_in_file_std(   const std::string&                  file_name)
{
	std::ifstream in(file_name.c_str());
	if (!in)
		throw std::runtime_error("Could not open " + file_name);
	return count_fasta(std::istreambuf_iterator<char>(in),
					  std::istreambuf_iterator<char>());
}

std::streamsize  estimate_fasta_seq_in_file(   const std::string&              file_name)
{
	try
	{
		io::mapped_file_source mapfile;
		mapfile.open(file_name);
		return count_fasta(mapfile.data(),  mapfile.data() + mapfile.size());
	}
	catch ( std::ios::failure& fail )
	{
		// probably file too large.
	}

	return count_lines_in_file_std( file_name);
}




